#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Modbus采集管理器
基于pymodbus 3.11.2实现，负责RTU和TCP两种协议的Modbus设备采集功能
确保所有操作不阻塞主流程
"""

import struct
import threading
from typing import Callable, Optional, Dict, Any, Tuple

# pymodbus 3.11.2 API
from pymodbus.client import ModbusTcpClient, ModbusSerialClient
from pymodbus import FramerType


class ModbusCollectionManager:
    """Modbus采集管理器 - 支持RTU和TCP协议，基于pymodbus 3.11.2"""

    def __init__(self, message_callback: Callable[[str, str, str], None]):
        """
        初始化Modbus采集管理器

        Args:
            message_callback: 消息回调函数 (message, level, msg_type)
        """
        self.message_callback = message_callback

        # RTU连接
        self.rtu_client = None
        self.rtu_connected = False

        # TCP连接
        self.tcp_client = None
        self.tcp_connected = False

        # 当前协议
        self.current_protocol = "RTU"  # RTU 或 TCP

    def _notify(self, message: str, level: str = "INFO", msg_type: str = "normal"):
        """发送通知消息"""
        self.message_callback(message, level, msg_type)

    def set_protocol(self, protocol: str):
        """设置当前协议"""
        self.current_protocol = protocol
        self._notify(f"🔄 切换到 {protocol} 协议", "INFO", "normal")

    # ==================== RTU 连接管理 ====================

    def connect_rtu(self, port: str, baudrate: int = 9600, databits: int = 8,
                   parity: str = 'N', stopbits: int = 1, timeout: float = 3.0) -> bool:
        """
        连接RTU串口设备（异步执行）

        Args:
            port: 串口名称
            baudrate: 波特率
            databits: 数据位
            parity: 校验位
            stopbits: 停止位
            timeout: 超时时间

        Returns:
            连接是否成功（立即返回True，实际连接结果通过回调通知）
        """
        # 在后台线程中执行连接操作，避免阻塞GUI
        def connect_in_background():
            try:
                # 先断开现有连接
                if self.rtu_client:
                    self.rtu_client.close()

                # 创建RTU客户端
                self.rtu_client = ModbusSerialClient(
                    port=port,
                    baudrate=baudrate,
                    bytesize=databits,
                    parity=parity,
                    stopbits=stopbits,
                    timeout=timeout,
                    framer=FramerType.RTU
                )

                # 尝试连接
                if self.rtu_client.connect():
                    self.rtu_connected = True
                    self.set_protocol("RTU")
                    self._notify(f"✅ RTU连接成功: {port}@{baudrate}", "SUCCESS", "normal")
                else:
                    self.rtu_connected = False
                    self._notify(f"❌ RTU连接失败: {port}@{baudrate}", "ERROR", "error")

            except Exception as e:
                self.rtu_connected = False
                self._notify(f"❌ RTU连接异常: {e}", "ERROR", "error")

        # 启动后台线程
        connect_thread = threading.Thread(target=connect_in_background, daemon=True)
        connect_thread.start()

        # 立即返回，不阻塞主流程
        self._notify(f"⏳ 正在连接RTU设备: {port}@{baudrate}...", "INFO", "normal")
        return True

    def disconnect_rtu(self):
        """断开RTU连接"""
        try:
            if self.rtu_client:
                self.rtu_client.close()
                self.rtu_client = None
            self.rtu_connected = False
            self._notify("🔌 RTU连接已断开", "INFO", "normal")
        except Exception as e:
            self._notify(f"❌ RTU断开异常: {e}", "ERROR", "error")

    def is_rtu_connected(self) -> bool:
        """检查RTU是否连接"""
        return self.rtu_connected and self.rtu_client and self.rtu_client.is_socket_open()

    # ==================== TCP 连接管理 ====================

    def connect_tcp(self, host: str, port: int, timeout: float = 3.0) -> bool:
        """
        连接TCP设备（异步执行）

        Args:
            host: IP地址
            port: 端口号
            timeout: 超时时间

        Returns:
            连接是否成功（立即返回True，实际连接结果通过回调通知）
        """
        # 在后台线程中执行连接操作，避免阻塞GUI
        def connect_in_background():
            try:
                # 先断开现有连接
                if self.tcp_client:
                    self.tcp_client.close()

                # 创建TCP客户端
                self.tcp_client = ModbusTcpClient(host=host, port=port, timeout=timeout)

                # 尝试连接
                if self.tcp_client.connect():
                    self.tcp_connected = True
                    self.set_protocol("TCP")
                    self._notify(f"✅ TCP连接成功: {host}:{port}", "SUCCESS", "normal")
                else:
                    self.tcp_connected = False
                    self._notify(f"❌ TCP连接失败: {host}:{port}", "ERROR", "error")

            except Exception as e:
                self.tcp_connected = False
                self._notify(f"❌ TCP连接异常: {e}", "ERROR", "error")

        # 启动后台线程
        connect_thread = threading.Thread(target=connect_in_background, daemon=True)
        connect_thread.start()

        # 立即返回，不阻塞主流程
        self._notify(f"⏳ 正在连接TCP设备: {host}:{port}...", "INFO", "normal")
        return True

    def disconnect_tcp(self):
        """断开TCP连接"""
        try:
            if self.tcp_client:
                self.tcp_client.close()
                self.tcp_client = None
            self.tcp_connected = False
            self._notify("🔌 TCP连接已断开", "INFO", "normal")
        except Exception as e:
            self._notify(f"❌ TCP断开异常: {e}", "ERROR", "error")

    def is_tcp_connected(self) -> bool:
        """检查TCP是否连接"""
        return self.tcp_connected and self.tcp_client and self.tcp_client.is_socket_open()

    # ==================== 指令解析 ====================

    def parse_modbus_command(self, cmd_str: str) -> Tuple[int, int, int, int, bytes]:
        """
        解析Modbus RTU格式指令

        Args:
            cmd_str: 指令字符串，如 "01 03 00 00 00 06"

        Returns:
            (unit_id, function_code, start_addr, quantity, rtu_with_crc)
        """
        try:
            parts = cmd_str.strip().split()
            if len(parts) < 6:
                raise ValueError("指令格式应为: 单元ID 功能码 起始地址H 起始地址L 数量H 数量L [CRCH CRCL]")

            unit_id = int(parts[0], 16)
            function_code = int(parts[1], 16)
            start_addr = (int(parts[2], 16) << 8) + int(parts[3], 16)
            quantity = (int(parts[4], 16) << 8) + int(parts[5], 16)

            # 构建RTU数据部分(不带CRC)
            rtu_data = bytes([unit_id, function_code]) + struct.pack(">HH", start_addr, quantity)

            # 检查是否已经包含CRC
            if len(parts) >= 8:
                provided_crc = bytes([int(parts[6], 16), int(parts[7], 16)])
                calculated_crc = self._calculate_crc(rtu_data)

                if provided_crc != calculated_crc:
                    self._notify(f"⚠️ 警告: 提供的CRC ({provided_crc.hex().upper()}) 与计算的CRC ({calculated_crc.hex().upper()}) 不匹配", "WARNING", "normal")

                crc = calculated_crc
            else:
                crc = self._calculate_crc(rtu_data)
                self._notify(f"🔧 自动计算CRC: {crc.hex().upper()}", "INFO", "normal")

            return unit_id, function_code, start_addr, quantity, rtu_data + crc

        except ValueError as e:
            raise ValueError(f"无效指令: {e}")

    def _calculate_crc(self, data: bytes) -> bytes:
        """计算Modbus RTU CRC校验码"""
        crc = 0xFFFF
        for byte in data:
            crc ^= byte
            for _ in range(8):
                if crc & 0x0001:
                    crc >>= 1
                    crc ^= 0xA001
                else:
                    crc >>= 1
        return struct.pack('<H', crc)  # 小端序

    def calculate_crc_for_command(self, cmd_str: str) -> Optional[str]:
        """
        为指令计算CRC

        Args:
            cmd_str: RTU指令字符串（十六进制格式）

        Returns:
            CRC字符串，如 "C5 C8"
        """
        try:
            # 解析指令（不包含CRC部分）
            parts = cmd_str.strip().split()
            if len(parts) < 6:
                return None

            # 只取前6个字节
            data_parts = parts[:6]
            data = bytes([int(part, 16) for part in data_parts])

            crc = self._calculate_crc(data)
            return f"{crc[0]:02X} {crc[1]:02X}"

        except Exception as e:
            self._notify(f"❌ CRC计算失败: {e}", "ERROR", "error")
            return None

    # ==================== 指令发送 ====================

    def send_command(self, cmd_str: str, auto_crc: bool = True) -> bool:
        """
        发送Modbus指令（根据当前协议自动选择，异步执行）

        Args:
            cmd_str: 指令字符串
            auto_crc: 是否自动计算CRC（仅RTU有效）

        Returns:
            发送是否成功（立即返回True，实际结果通过回调通知）
        """
        # 在后台线程中执行发送操作，避免阻塞GUI
        def send_in_background():
            try:
                if self.current_protocol == "RTU":
                    self._send_rtu_command_sync(cmd_str, auto_crc)
                else:  # TCP
                    self._send_tcp_command_sync(cmd_str)
            except Exception as e:
                self._notify(f"❌ 发送指令异常: {e}", "ERROR", "error")

        # 启动后台线程
        send_thread = threading.Thread(target=send_in_background, daemon=True)
        send_thread.start()

        self._notify("⏳ 正在发送指令...", "INFO", "send")
        return True

    def _send_rtu_command_sync(self, cmd_str: str, auto_crc: bool = True) -> bool:
        """发送RTU指令（同步）"""
        if not self.is_rtu_connected():
            self._notify("❌ RTU未连接", "ERROR", "error")
            return False

        try:
            # 解析指令
            unit_id, function_code, start_addr, quantity, rtu_with_crc = self.parse_modbus_command(cmd_str)

            # 详细日志输出
            self._notify(f"📤 解析Modbus指令: 从站{unit_id}, 功能码{function_code}, 地址{start_addr}, 数量{quantity}", "INFO", "send")
            self._notify(f"📤 发送RTU指令: {rtu_with_crc.hex().upper()}", "INFO", "send")

            # 发送指令
            result = self.rtu_client.execute(rtu_with_crc)

            if result.isError():
                self._notify(f"❌ RTU响应错误: {result}", "ERROR", "error")
                return False
            else:
                self._notify(f"✅ RTU指令发送成功", "SUCCESS", "send")
                self._parse_rtu_response(result, function_code, unit_id)
                return True

        except Exception as e:
            self._notify(f"❌ RTU指令发送失败: {e}", "ERROR", "error")
            return False

    def _send_tcp_command_sync(self, cmd_str: str) -> bool:
        """发送TCP指令（同步）"""
        if not self.is_tcp_connected():
            self._notify("❌ TCP未连接", "ERROR", "error")
            return False

        try:
            # 解析指令
            unit_id, function_code, start_addr, quantity, rtu_with_crc = self.parse_modbus_command(cmd_str)

            # 详细日志输出
            self._notify(f"📤 解析Modbus指令: 从站{unit_id}, 功能码{function_code}, 地址{start_addr}, 数量{quantity}", "INFO", "send")

            # 使用高级接口发送
            return self._send_tcp_high_level(function_code, start_addr, quantity, unit_id)

        except Exception as e:
            self._notify(f"❌ TCP指令发送失败: {e}", "ERROR", "error")
            return False

    def _send_tcp_high_level(self, function_code: int, start_addr: int, quantity: int, unit_id: int) -> bool:
        """使用高级接口发送TCP指令"""
        try:
            # 根据功能码执行相应操作
            result = None
            if function_code == 1:  # 读取线圈
                result = self.tcp_client.read_coils(start_addr, count=quantity, device_id=unit_id)
            elif function_code == 2:  # 读取离散输入
                result = self.tcp_client.read_discrete_inputs(start_addr, count=quantity, device_id=unit_id)
            elif function_code == 3:  # 读取保持寄存器
                result = self.tcp_client.read_holding_registers(start_addr, count=quantity, device_id=unit_id)
            elif function_code == 4:  # 读取输入寄存器
                result = self.tcp_client.read_input_registers(start_addr, count=quantity, device_id=unit_id)
            elif function_code == 5:  # 写单个线圈
                result = self.tcp_client.write_coil(start_addr, bool(quantity), device_id=unit_id)
            elif function_code == 6:  # 写单个寄存器
                result = self.tcp_client.write_register(start_addr, quantity, device_id=unit_id)
            else:
                self._notify(f"❌ 不支持的功能码: {function_code}", "ERROR", "error")
                return False

            # 处理响应
            if result.isError():
                self._notify(f"❌ TCP响应错误: {result}", "ERROR", "error")
                return False
            else:
                self._notify(f"✅ TCP指令发送成功", "SUCCESS", "send")
                self._parse_tcp_response(result, function_code, unit_id)
                return True

        except Exception as e:
            self._notify(f"❌ 高级接口TCP发送失败: {e}", "ERROR", "error")
            return False

    # ==================== 响应解析 ====================

    def _parse_rtu_response(self, result, function_code: int, unit_id: int):
        """解析RTU响应"""
        try:
            # 显示详细数据
            if hasattr(result, 'registers'):
                self._notify(f"📊 从站{unit_id} 寄存器值: {result.registers}", "INFO", "receive")
                # 显示十六进制格式
                hex_values = [f"0x{val:04X}" for val in result.registers]
                self._notify(f"📊 十六进制格式: {hex_values}", "INFO", "receive")
            elif hasattr(result, 'bits'):
                self._notify(f"📊 从站{unit_id} 位值: {result.bits}", "INFO", "receive")
            else:
                self._notify(f"📊 从站{unit_id} 操作完成", "INFO", "receive")

        except Exception as e:
            self._notify(f"❌ RTU响应解析失败: {e}", "ERROR", "error")

    def _parse_tcp_response(self, result, function_code: int, unit_id: int):
        """解析TCP响应"""
        try:
            # 显示详细数据
            if hasattr(result, 'registers'):
                self._notify(f"📊 从站{unit_id} 寄存器值: {result.registers}", "INFO", "receive")
                # 显示十六进制格式
                hex_values = [f"0x{val:04X}" for val in result.registers]
                self._notify(f"📊 十六进制格式: {hex_values}", "INFO", "receive")
            elif hasattr(result, 'bits'):
                self._notify(f"📊 从站{unit_id} 位值: {result.bits}", "INFO", "receive")
            else:
                self._notify(f"📊 从站{unit_id} 操作完成", "INFO", "receive")

        except Exception as e:
            self._notify(f"❌ TCP响应解析失败: {e}", "ERROR", "error")
